/*
 * clerk.cc
 *
 *  Created on: May 31, 2011
 *      Author: peckb1
 */

#include "clerk.h"

Clerk::Clerk(Locks* allTheLocks) {
    haveAJob = TRUE;
    theLocks = allTheLocks;
}

Clerk::~Clerk() {
}

void
Clerk::HelpCustomerCheckout(bool customerWantsToReturn) {
	//Clerk has already acquired his lock. Customer already has
	//provided his info and has put one item on the desk
	//clerk needs to scan it (if any provided) and let the customer know
	//he is waiting for other items.
	//if customer hasnt provided any item, or is done with items lets proceed to next step.
	Lock* clerkLock = theLocks->_clerkLock[currentRegister];
	Lock* registerLock = theLocks->_fromRegisterInteractionLock[currentRegister];
	Lock* registerStatusLock = theLocks->_registerLock[currentRegister];
	Lock* peopleWaiting = theLocks->_peopleWaitingForCashRegiterLock[currentRegister];
	//First lets acquire the register lock
#ifndef IGNORE_REGISTERS
	#ifdef PRINT_EXTRA_LOG_CLERK
		printf("CLERK# %i:Lets acquire the register\n",currentRegister);
	#endif
	registerStatusLock->Acquire();
	while (theLocks->_managerEmtpyingTill[currentRegister] != -1)
	{
		//Cash register hasn't started to work yet
		//lets wait for it to startup
		#ifdef PRINT_EXTRA_LOG_CLERK
			printf("CLERK# %i:Cash regiter is busy, lets wait\n",currentRegister);
		#endif
		peopleWaiting->Acquire(); //NEW
		registerStatusLock->Release();
		do
		{
			theLocks->_clerkWantsToInteract[currentRegister] = 0;
			(theLocks->_peopleWaitingForCashRegister[currentRegister])++;
			theLocks->_peopleWaitingForCashRegiterCV[currentRegister]->Wait(peopleWaiting);
		} while(theLocks->_clerkWantsToInteract[currentRegister] != 1);
		theLocks->_clerkWantsToInteract[currentRegister] = -1;
		registerStatusLock->Acquire();
		peopleWaiting->Release();
	}
	#ifdef PRINT_EXTRA_LOG_CLERK
		printf("CLERK# %i:Let the register know we want to scan items\n",currentRegister);
	#endif
	theLocks->_registerCV[currentRegister]->Signal(registerStatusLock);
	registerLock->Acquire();
	theLocks->_managerEmtpyingTill[currentRegister] = 0;
	registerStatusLock->Release();
	#ifdef PRINT_EXTRA_LOG_CLERK
		printf("CLERK# %i:Lets wait for cash regiter until it is ready\n",currentRegister);
	#endif
	//Lets wait for cash register till announce it is ready to accept items
	theLocks->_fromRegisterInteractionCV[currentRegister]->Wait(registerLock);
	//Register is ready to accept our items.
#endif

	int totalItemsProvidedCount = 0;
	while (theLocks->_nextItem[currentRegister] != NOT_FOUND)
	{
		int itemIdpassed = theLocks->_nextItem[currentRegister];
		totalItemsProvidedCount ++;
		#ifdef PRINT_OUTPUT_CLERK
			printf("Clerk %i got item [%i] from Customer [%i].\n",currentRegister,itemIdpassed,theLocks->_customerBeingHelped[currentRegister]);
		#endif
		#ifndef IGNORE_REGISTERS
		///////////////////////////////////////////////////////////////////////
		/// Start of Cash Register Interaction
		///////////////////////////////////////////////////////////////////////
			//Lets put the item into cash register
			if (customerWantsToReturn)
			{
				theLocks->_itemToRefund[currentRegister] = theLocks->itemsInStore[itemIdpassed];
			}
			else
			{
				theLocks->_itemToScan[currentRegister] = theLocks->itemsInStore[itemIdpassed];
			}
			theLocks->_toRegisterInteractionCV[currentRegister]->Signal(registerLock);
			//Lets wait for cash register till announces it is ready to accept items
			#ifdef PRINT_OUTPUT_CLERK
				printf("Clerk %i gave item [%i] to Cash Register [%i] for scanning.\n",currentRegister,itemIdpassed,currentRegister);
			#endif
			theLocks->_fromRegisterInteractionCV[currentRegister]->Wait(registerLock);
		///////////////////////////////////////////////////////////////////////
		/// End of Cash Register Interaction
		///////////////////////////////////////////////////////////////////////
		#endif
		theLocks->_customerCV[currentRegister]->Signal(clerkLock);
		#ifdef PRINT_EXTRA_LOG_CLERK
			printf("CLERK# %i: Lets wait for the next item.\n",currentRegister);
		#endif
		theLocks->_clerkCV[currentRegister]->Wait(clerkLock);
		#ifdef PRINT_EXTRA_LOG_CLERK
			printf("CLERK# %i: Customer just signaled that the next item is ready\n",currentRegister);
		#endif
	}
	#ifdef PRINT_EXTRA_LOG_CLERK
		if (totalItemsProvidedCount==0)
			printf("CLERK# %i: Customer hasn't passed any items.\n",currentRegister);
		else
			printf("CLERK# %i: Customer has passed all of his items.\n",currentRegister);
	#endif
	#ifdef PRINT_EXTRA_LOG_CLERK
		printf("CLERK# %i: Get the totals from cash register\n",currentRegister);
	#endif
	//Now we need to calculate the total
	double totalTransactionAmount = 0;
	#ifndef IGNORE_REGISTERS
	///////////////////////////////////////////////////////////////////////
	/// Start of Cash Register Interaction
	///////////////////////////////////////////////////////////////////////
		theLocks->_itemToRefund[currentRegister] = NULL;
		theLocks->_itemToScan[currentRegister] = NULL;
		#ifdef PRINT_EXTRA_LOG_CLERK
			printf("CLERK# %i signal cash register to give us the total\n", currentRegister);
		#endif
		theLocks->_toRegisterInteractionCV[currentRegister]->Signal(registerLock);
		//Lets wait for cash register till it calculates the total
		#ifdef PRINT_EXTRA_LOG_CLERK
			printf("CLERK# %i wait for cash register till it calculates the total amount\n", currentRegister);
		#endif
		theLocks->_fromRegisterInteractionCV[currentRegister]->Wait(registerLock);
		// Grab the total from cash register
		totalTransactionAmount = theLocks->_moneyToTransfer[currentRegister];
	///////////////////////////////////////////////////////////////////////
	/// End of Cash Register Interaction
	///////////////////////////////////////////////////////////////////////
	#endif
	#ifdef PRINT_OUTPUT_CLERK
		printf("Clerk [%i] got total cost $[%.2f] from Cash Register [%i].\n",currentRegister,totalTransactionAmount,currentRegister);
	#endif
	// Signal the customer to pay/receive the money
	theLocks->_customerCV[currentRegister]->Signal(clerkLock);
	//Wait till customer pay his bill or pickup his refund
	#ifdef PRINT_OUTPUT_CLERK
		printf("Clerk [%i] tells Customer [%i] total cost is $[%.2f].\n",currentRegister,theLocks->_customerBeingHelped[currentRegister],totalTransactionAmount);
	#endif
	theLocks->_clerkCV[currentRegister]->Wait(clerkLock);
	//Pickup the money paid
	#ifdef PRINT_OUTPUT_CLERK
		if (customerWantsToReturn)
		{
			printf("Clerk [%i] got money [%.2f] from Cash Register [%i].\n",currentRegister,totalTransactionAmount,currentRegister);
		}
		else
		{
			printf("Clerk [%i] got money [%.2f] from Customer [%i].\n",currentRegister,totalTransactionAmount, theLocks->_customerBeingHelped[currentRegister]);
		}
	#endif
	//Wait for the receipt from cash register
	#ifndef IGNORE_REGISTERS
	///////////////////////////////////////////////////////////////////////
	/// Start of Cash Register Interaction
	///////////////////////////////////////////////////////////////////////
		//Let the cash register know the amount was processed
		//and receipt is required
		theLocks->_toRegisterInteractionCV[currentRegister]->Signal(registerLock);
#ifdef PRINT_OUTPUT_CLERK
if (customerWantsToReturn)
{
	printf("Clerk [%i] gave money [%.2f] to Customer [%i].\n",currentRegister,totalTransactionAmount, theLocks->_customerBeingHelped[currentRegister]);
}
else
{
	printf("Clerk [%i] gave money [%.2f] to Cash Register [%i].\n",currentRegister,totalTransactionAmount, currentRegister);
}
#endif
		theLocks->_fromRegisterInteractionCV[currentRegister]->Wait(registerLock);
#ifdef PRINT_OUTPUT_CLERK
	printf("Clerk [%i] got receipt from Cash Register[%i].\n",currentRegister,currentRegister);
#endif
	///////////////////////////////////////////////////////////////////////
	/// End of Cash Register Interaction
	///////////////////////////////////////////////////////////////////////
	#endif
	//and let the customer know he can pickup the receipt
	theLocks->_customerCV[currentRegister]->Signal(clerkLock);
	//Wait till customer pickup the receipt (and payment if it was a refund)
#ifdef PRINT_OUTPUT_CLERK
	printf("Clerk [%i] gave the receipt to Customer [%i] and tells him to leave.\n",currentRegister, theLocks->_customerBeingHelped[currentRegister]);
#endif

#ifdef PRINT_EXTRA_LOG_CLERK
		printf("CLERK# %i: Wait till customer picks up the receipt\n",currentRegister);
#endif
	theLocks->_clerkCV[currentRegister]->Wait(clerkLock);
	//We are done with this customer, lets release our lock
#ifdef PRINT_EXTRA_LOG_CLERK
		printf("CLERK# %i: Done with this customer!!!\n",currentRegister);
#endif
	#ifndef IGNORE_REGISTERS
		#ifdef PRINT_EXTRA_LOG_CLERK
			printf("CLERK# %i:Release the register lock\n",currentRegister);
		#endif
		registerLock->Release();
	#endif
}

void Clerk::FinishWithScanningCustomer(bool customerWantsToReturn) {
    }





void
Clerk::GoToWork(int lineId) {
    currentRegister = lineId;
    while(haveAJob) {
        // make sure no one can get in line while we help our customer
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLineLock->Acquire()\n", currentRegister);
#endif
        theLocks->_clerkLinelock->Acquire();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLineLock->Acquire() Done!\n", currentRegister);
#endif
        if(theLocks->_clerkLineLength[currentRegister] > 0) {
			#ifdef PRINT_EXTRA_LOG_CLERK
					printf("CLERK# %i: founds out there are %i customers waiting for him \n",currentRegister, theLocks->_clerkLineLength[currentRegister]);
			#endif
            // I have some customers in my line
            if(theLocks->_clerkLineLength[currentRegister] > 4) {
				#ifdef PRINT_OUTPUT_CLERK
            		printf("Clerk [%i] see more than 4 people in line and calls Manager.\n",currentRegister);
				#endif




#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i _signalManagerClerkHelp->Acquire()\n", currentRegister);
#endif
            	theLocks->_signalManagerClerkHelp->Acquire();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i _signalManagerClerkHelp->Acquire() Done!\n", currentRegister);
#endif
                theLocks->_storeNeedsMoreClerks[currentRegister] = true;
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i _signalManagerClerkHelp->Release()\n", currentRegister);
#endif
                theLocks->_signalManagerClerkHelp->Release();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i _signalManagerClerkHelp->Release() Done!\n", currentRegister);
#endif
            }
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i _clerkLine[%i][clerkLineLock]->Signal()\n", currentRegister, currentRegister);
#endif
            theLocks->_clerkLineCV[currentRegister]->Signal(theLocks->_clerkLinelock);
            theLocks->_clerkLineLength[currentRegister]--;
            Lock* clerkLock = theLocks->_clerkLock[currentRegister];
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLock->Acquire()\n", currentRegister);
#endif
            clerkLock->Acquire();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLock->Acquire() Done!\n", currentRegister);
#endif
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLineLock->Release()\n", currentRegister);
#endif
		theLocks->_clerkLinelock->Release();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLineLock->Release() Done!\n", currentRegister);
#endif
            //The customer is in sleep, waiting for our signal so he can
            //provide his information (returning or purchase)
			#ifdef PRINT_EXTRA_LOG_CLERK
					printf("CLERK# %i: Waits till customer tell him he is ready to pass the items\n",currentRegister);
			#endif
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkCV[%i][clerkLock]->Wait()\n", currentRegister, currentRegister);
#endif
			theLocks->_clerkCV[currentRegister]->Wait(clerkLock);
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkCV[%i][clerkLock]->Wait() Done!\n", currentRegister, currentRegister);
#endif

            //Customer just signaled us that he is ready to pass
            //the items to us
			#ifdef PRINT_EXTRA_LOG_CLERK
					printf("CLERK# %i: Customer announced he is read to pass the items\n",currentRegister);
			#endif
            theLocks->_customerActionLock->Acquire();
			bool customerWantsToReturn = theLocks->_customerWantsToScanItems[currentRegister];
			theLocks->_customerActionLock->Release();
            HelpCustomerCheckout(customerWantsToReturn);
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLock->Release()!\n", currentRegister);
#endif
            clerkLock->Release();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLock->Release() Done!\n", currentRegister);
#endif
            haveAJob = true;
        } else {
            // time to go on break - all clerks are allowed to go on break
			#ifdef PRINT_OUTPUT_CLERK
            	printf("Clerk [%i] is leaving Cash Register [%i] and going on break.\n", currentRegister, currentRegister);
			#endif
            // set our status to on break
            theLocks->_clerkStatus[currentRegister] = CLERK_BREAK;
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLineLock->Release()!\n", currentRegister);
#endif
            theLocks->_clerkLinelock->Release();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkLineLock->Release() Done!\n", currentRegister);
#endif
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkOnBreakLock->Acquire()!\n", currentRegister);
#endif
            // get the going on break lock
            theLocks->_clerksOnBreakLock->Acquire();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkOnBreakLock->Acquire() Done!\n", currentRegister);
#endif

            // increment the number of clerks on break
            theLocks->_clerksOnBreakMV ++;
            // wait to be woken up
			#ifdef PRINT_EXTRA_LOG_CLERK
					printf("CLERK# %i: Wait for manager to wake me up\n",currentRegister);
			#endif
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerksOnBreakCV->Wait()\n", currentRegister);
#endif

			theLocks->_clerksOnBreakCV->Wait(theLocks->_clerksOnBreakLock);
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerksOnBreakCV->Wait() Done!\n", currentRegister);
#endif

            // we're no longer on break, lets get busy!
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerksLineLock->Acquire()\n", currentRegister);
#endif
            theLocks->_clerkLinelock->Acquire();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerksLineLock->Acquire() Done!\n", currentRegister);
#endif
            theLocks->_clerkStatus[currentRegister] = CLERK_BUSY;
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerksLineLock->Release()\n", currentRegister);
#endif
            theLocks->_clerkLinelock->Release();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerksLineLock->Release done!()\n", currentRegister);
#endif

			#ifdef PRINT_OUTPUT_CLERK
            	printf("Clerk [%i] was called by Manager to work.\n", currentRegister);
			#endif
            Lock* waitForClerksToReturn = theLocks->_customerNeedsClerkAssitanceLock;
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i ClerkOnBreakLock->Release()\n", currentRegister);
#endif
            theLocks->_clerksOnBreakLock->Release();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i clerkOnBreakLock->Release() done!\n", currentRegister);
#endif




#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i waitForClerksToReturn->Acquire()\n", currentRegister);
#endif
            waitForClerksToReturn->Acquire();
#ifdef PRINT_LOCKS_CLERK
	printf("CLERK# %i waitForClerksToReturn->Acquire() Done!\n", currentRegister);
#endif




            //See if any customer was waiting for us
			#ifdef PRINT_EXTRA_LOG_CLERK
					printf("CLERK# %i: Just got back from break.\n",currentRegister);
			#endif
            int totalSignaled = 0;
			while (theLocks->_customersNeedingAsistanceMV > 0 && totalSignaled < 5)
			{
				theLocks->_customerNeedsClerkAssitanceCV->Signal(waitForClerksToReturn);
				theLocks->_customersNeedingAsistanceMV --;
				theLocks->_clerkIDSignalingToCustomer = currentRegister;
				totalSignaled++;
				#ifdef PRINT_EXTRA_LOG_CLERK
						printf("CLERK# %i: Just signaled a client to wake up.\n",currentRegister);
				#endif
				theLocks->_customerShouldFindTheClerkCV->Wait(waitForClerksToReturn);
				#ifdef PRINT_EXTRA_LOG_CLERK
						printf("CLERK# %i: client answered back that he is in line\n",currentRegister);
				#endif
			}
			#ifdef PRINT_EXTRA_LOG_CLERK
				printf("CLERK# %i: Just signlaed %i customers to come into line\n",currentRegister, totalSignaled);
			#endif
			waitForClerksToReturn->Release();
        }
    }
}
